package raytracer;

/* Simple 3D vector implementation */

public class Vector3f {
	public float x;
	public float y;
	public float z;
	
	public Vector3f(Vector3f vec) {
		this.x = vec.x;
		this.y = vec.y;
		this.z = vec.z;
	}

	public Vector3f(float x, float y, float z) {
		this.x = x;
		this.y = y;
		this.z = z;
	}

	public Vector3f() {
	}

	static Vector3f fromPoints(Vector3f origin, Vector3f target) {
		return new Vector3f(target.x - origin.x, target.y - origin.y, target.z - origin.z);
	}

	public float length() {
		return (float) Math.sqrt(x * x + y * y + z * z);
	}
	
	public float lengthSquared() {
		return x * x + y * y + z * z;
	}

	public Vector3f normalize() {
		Vector3f r = new Vector3f(this);
		r.normalizeLocal();
		return r;
	}

	public Vector3f normalizeLocal() {
		float l = length();
		this.x /= l;
		this.y /= l;
		this.z /= l;
		return this;
	}

	public float dot(Vector3f vec) {
		return this.x * vec.x + this.y * vec.y + this.z * vec.z;
	}

	public Vector3f scaleLocal(float scalar) {
		this.x *= scalar;
		this.y *= scalar;
		this.z *= scalar;
		return this;
	}

	public Vector3f scale(float scalar) {
		Vector3f r = new Vector3f(this);
		r.scaleLocal(scalar);
		return r;
	}

	public Vector3f addLocal(Vector3f vec) {
		this.x += vec.x;
		this.y += vec.y;
		this.z += vec.z;
		return this;
	}

	public Vector3f addLocal(float s) {
		this.x += s;
		this.y += s;
		this.z += s;
		return this;
	}

	public Vector3f add(Vector3f vec) {
		Vector3f r = new Vector3f(this);
		r.addLocal(vec);
		return r;
	}

	public Vector3f subtractLocal(Vector3f vec) {
		this.x -= vec.x;
		this.y -= vec.y;
		this.z -= vec.z;
		return this;
	}

	public Vector3f subtract(Vector3f vec) {
		Vector3f r = new Vector3f(this);
		r.subtractLocal(vec);
		return r;
	}
	
	public float distance(Vector3f vec) {
		return this.subtract(vec).length();
	}

	public Vector3f crossLocal(Vector3f vec) {
		Vector3f c = cross(vec);
		this.x = c.x;
		this.y = c.y;
		this.z = c.z;
		return this;
	}

	public Vector3f cross(Vector3f vec) {
		return new Vector3f(this.y * vec.z - this.z * vec.y, this.z * vec.x - this.x * vec.z, this.x * vec.y - this.y
				* vec.x);
	}

	public Vector3f negate() {
		Vector3f r = new Vector3f(this);
		r.scale(-1);
		return r;
	}

	public Vector3f negateLocal() {
		this.scaleLocal(-1);
		return this;
	}

	@Override
	public String toString() {
		return "(" + x + ", " + y + ", " + z + ")";
	}

	public boolean isZero() {
		return x == 0 && y == 0 && z == 0;
	}

	public void copyFrom(Vector3f other) {
		this.x = other.x;
		this.y = other.y;
		this.z = other.z;
	}
	
	@Override
	public boolean equals(Object obj) {
		Vector3f v = (Vector3f) obj;
		return this.x == v.x && this.y == v.y && this.z == v.z;
	}
	
	@Override
	public int hashCode() {
		return ((Float) x).hashCode() + ((Float) y).hashCode() + ((Float) z).hashCode();
	}
}
